/*
 * =====================================================================================
 *
 *       Filename:  download_file.c
 *
 *    Description:  
 *
 *        Version:  1.0
 *        Created:  03/19/2015 02:53:06 PM
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  Sun Jun
 *   Organization:  
 *
 * =====================================================================================
 */
#include "download.h"
#include <libavutil/avutil.h>
#include <libavfilter/avfilter.h>
#include <libavdevice/avdevice.h>
#include <libavcodec/avcodec.h>
#include <libavfilter/buffersrc.h>
#include <libavutil/buffer.h>
//#include <libavutil/buffer_internal.h>
#include <unistd.h>




static int download_thread(void *data);
static int download_thread_temp(void *data);
static size_t write_packet_to_file(AVPacket *packet, Download_context *download_ctx);
static FILE *open_file_read(const char *file);
static FILE *open_file_write(const char *file);
static void close_file(FILE *file);

//extern ffmpeg_func_t ffmpeg_func;
extern Download_context *download_context_g;
extern Download_context *download_context_temp;
extern void init_download_context_callback(Download_context *context);
extern int av_gettime(void);

extern CMPCoreData *coredata_g;

AVBuffer *avbuffer;
Download_context *init_download(const char *file_url, const char *file_path, const char *file_packetet)
{
    Download_context *download_ctx = NULL; 
    download_ctx = malloc(sizeof(Download_context));
    memset(download_ctx, 0, sizeof(Download_context));

    if (download_ctx == NULL)
        return NULL;
    
    if (file_url)
        download_ctx->network_url     = strdup(file_url);
    if (file_path)
        download_ctx->local_file_path = strdup(file_path);

    if (file_packetet) {
        download_ctx->local_packet_file_path = strdup(file_packetet);
        download_ctx->need_write_packet = 1;
        download_ctx->packet_file_write = open_file_write(download_ctx->local_packet_file_path);
        download_ctx->packet_file_read = open_file_read(download_ctx->local_packet_file_path);
        download_ctx->packet_array = (Packet_data *)malloc(PACKET_SIZE * sizeof(Packet_data));
        memset(download_ctx->packet_array, 0, PACKET_SIZE * sizeof(Packet_data));
        download_ctx->packet_array_size = PACKET_SIZE;
    }

    download_ctx->mutex = (pthread_mutex_t *) malloc(sizeof(pthread_mutex_t));
    memset(download_ctx->mutex, 0, sizeof(pthread_mutex_t));
    pthread_mutex_init(download_ctx->mutex, NULL);

    download_ctx->cond = (pthread_cond_t*) malloc(sizeof(pthread_cond_t));
    memset(download_ctx->cond, 0, sizeof(pthread_cond_t));
    pthread_cond_init(download_ctx->cond, NULL);

    return download_ctx;
}

#define MY_FREE(x) if (x) \
{ \
    free(x); \
    x = NULL; \
} \


void deinit_download(Download_context *download_ctx)
{
    if (download_ctx) {
        set_download_status(download_ctx, DOWNLOAD_STOP);

        if(download_ctx->on_buffering)
        	download_ctx->on_buffering();
//        pthread_cancel(download_ctx->download_tid);
        if(download_ctx->download_tid)
            pthread_join(download_ctx->download_tid, NULL);
//        pthread_kill(download_ctx->download_tid, SIGQUIT);

//        LOG("deinit_download...1");
        if(download_ctx->local_packet_file_path && !download_ctx->has_pkt_file_removed){
//            LOG("deinit_download... has_pkt_file_removed = %d", download_ctx->has_pkt_file_removed);
	        int delete_result = remove(download_ctx->local_packet_file_path);
//            LOG("deinit_download delete_result=%d",delete_result);
        }

//        LOG("deinit_download...2");
        MY_FREE(download_ctx->network_url);
        MY_FREE(download_ctx->local_file_path);
        MY_FREE(download_ctx->local_packet_file_path);
        MY_FREE(download_ctx->packet_array);

        if (download_ctx->cond) {
            pthread_cond_destroy(download_ctx->cond);
            free(download_ctx->cond);
            download_ctx->cond = NULL;
        }   

        if (download_ctx->mutex) {
            pthread_mutex_destroy(download_ctx->mutex);
            free(download_ctx->mutex);
            download_ctx->mutex = NULL;
        }

        close_file(download_ctx->packet_file_read);
        close_file(download_ctx->packet_file_write);

        free(download_ctx);
    }
}


char* str_join(char *s1, char *s2)
{
    char *result = malloc(strlen(s1)+strlen(s2)+1);
    if (result == NULL) exit (1);

    strcpy(result, s1);
    strcat(result, s2);

    return result;
}

static void write_existed(char *out_file, AVFormatContext *ofmt_ctx, Download_context *d_ctx) {
	AVIOContext *input, *output = ofmt_ctx->pb;
	int ret;
	char error[500];
	char tmp[5]=".tmp";

	char *in_file_tmp = str_join(out_file,tmp);
	ret = avio_open(&input, in_file_tmp, AVIO_FLAG_READ);
	av_strerror(ret, error, 500);
//	LOG("download_streaming-->avformat_open_input res=%s...", error);
	if (ret) {
		goto fail;
	}
	while (1 && d_ctx) {
        pthread_mutex_lock(d_ctx->mutex);
        if (d_ctx->status == DOWNLOAD_STOP)
            break;

        while (d_ctx->status == DOWNLOAD_PAUSE) {
            pthread_cond_wait(d_ctx->cond, d_ctx->mutex);
        }
        pthread_mutex_unlock(d_ctx->mutex);

		uint8_t buf[1024];
		int n;
		n = avio_read(input, buf, sizeof(buf));
//		LOG("download_streaming-->read %d byte...", n);
		if (n <= 0)
			break;
		avio_write(output, buf, n);
		avio_flush(output);
	}

	avio_flush(output);

fail: 
	avio_close(input);
}

static void rewrite_exist_file(char *out_file, AVFormatContext *ofmt_ctx, Download_context *d_ctx) {
	AVFormatContext *ifmt_ctx_exist = NULL;
	AVPacket pkt;
	char error[500];
	int ret;
	char tmp[5]=".tmp";
	char *in_file_tmp=str_join(out_file,tmp);
	if ((ret = avformat_open_input(&ifmt_ctx_exist, in_file_tmp, 0, 0))
			< 0) {
		av_strerror(ret, error, 500);
//		LOG("download_streaming-->avformat_open_input res=%s...", error);
		goto end;
	}

	if ((ret = avformat_find_stream_info(ifmt_ctx_exist, 0)) < 0) {
		fprintf(stderr, "Failed to retrieve input stream information");
		av_strerror(ret, error, 500);
//		LOG("download_streaming-->avformat_find_stream_info res=%s...", error);
		goto end;
	}

	av_dump_format(ifmt_ctx_exist, 0, out_file, 0);
	avformat_write_header(ofmt_ctx, NULL);
	while (1 && d_ctx) {
        pthread_mutex_lock(d_ctx->mutex);
        if (d_ctx->status == DOWNLOAD_STOP)
            break;

        while (d_ctx->status == DOWNLOAD_PAUSE) {
            pthread_cond_wait(d_ctx->cond, d_ctx->mutex);
        }
        pthread_mutex_unlock(d_ctx->mutex);

		ret = av_read_frame(ifmt_ctx_exist, &pkt);
		if (ret < 0) {
			break;
		}
		ret = av_interleaved_write_frame(ofmt_ctx, &pkt);
		av_packet_unref(&pkt);
	}
end:
	avformat_close_input(&ifmt_ctx_exist);
	if (d_ctx->write_exist_finished)
		d_ctx->write_exist_finished();
}

//return 0 is success
int start_download(Download_context *download_ctx)
{
    int ret = -1;
    if (download_ctx) {
        if (download_ctx->type == DOWNLOAD_DEFAULT)
            ret = pthread_create(&download_ctx->download_tid, NULL, download_thread, (void *)download_ctx);
        else 
            ret = pthread_create(&download_ctx->download_tid, NULL, download_thread_temp, (void *)download_ctx);

    }
    return ret;
}
static int interrupt_cb(void *ctx)
{
	if(ctx){
	    Download_context* download_data = (Download_context*)ctx;
//	    long current = (long)(av_gettime() / 1000);
       long current = (long)(av_gettime()/ 1000);
        if ((download_data->status == DOWNLOAD_STOP)//得打开input之后再关闭   && download_data->is_input_opened == 1
        		|| (download_data->timeout > 0 && (current - download_data->timestamp > download_data->timeout))){
            return 1;
        }
	}
    return 0;
}

static int download_thread_temp(void *data)
{
	int i, ret = -1;
    Download_context *d_ctx = (Download_context *)data;
    if (d_ctx == NULL)
        return ret;

    AVFormatContext *ifmt_ctx = NULL;
    AVPacket pkt;
    const char *in_filename, *out_filename;
	char error[500];
	AVDictionary* options = NULL;

    in_filename  = d_ctx->network_url;
    out_filename = d_ctx->local_file_path;
    int current_downloaded_play_time;
//    LOG("download_thread_temp before register");

    av_register_all();

    while(!(coredata_g && coredata_g->avf_ctx && coredata_g->is_opened)){
        usleep(50*1000);
        if (d_ctx->status == DOWNLOAD_STOP)
    	    return 0;
    }

    if (!coredata_g)
    	return ret;

    coredata_g->has_download_stopped = 0;
    ifmt_ctx = coredata_g->avf_ctx;
////////////////与cmplayer共用一个，下边close也注释了，如要改回去。。。////////////////
//    ifmt_ctx = avformat_alloc_context();
//    ifmt_ctx->interrupt_callback.callback = interrupt_cb;
//    ifmt_ctx->interrupt_callback.opaque = d_ctx;
//    if(d_ctx->timeout > 1000){
//    	d_ctx->timestamp = (long)(  av_gettime() / 1000);
//		LOG("download_thread_temp---current=%ld,timeout=%d",d_ctx->timestamp,d_ctx->timeout);
//    }
//
////    av_dict_set(&options, "rtsp_transport", "udp", 0);
//
//    LOG("download_thread_temp 1,in_filename=%s",in_filename);
//    if ((ret = avformat_open_input(&ifmt_ctx, in_filename, 0, 0)) < 0) {//&options
//		av_strerror(ret, error, 500);
//		LOG("download_thread_temp-->avformat_open_input res=%d,%s...", ret, error);
//		goto end;
//    }
//
////    d_ctx->is_input_opened = 1;
//
//    ifmt_ctx->max_analyze_duration2 = 1000000;
//    ifmt_ctx->probesize2 = 1000000;
//    d_ctx->timestamp = (long)(  av_gettime() / 1000);
//    LOG("download_thread_temp 2");
//    if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0) {
//		av_strerror(ret, error, 500);
//		LOG("download_thread_temp-->avformat_find_stream_info res=%s...", error);
//		goto end;
//    }
//    d_ctx->timestamp = (long)(  av_gettime() / 1000);
//    LOG("download_thread_temp 3");
//    av_dump_format(ifmt_ctx, 0, in_filename, 0);

    d_ctx->streaming_duration = ifmt_ctx->duration / AV_TIME_BASE;

    if(!d_ctx->is_live){
		int64_t seek_time_int64t = 0;
		for (i = 0; i < ifmt_ctx->nb_streams; i++) {
			AVStream *in_stream = ifmt_ctx->streams[i];
			if (in_stream->codec->codec_type == AVMEDIA_TYPE_VIDEO){
				if (d_ctx->last_dts > 0) {
					seek_time_int64t = d_ctx->last_dts;
				}else if (d_ctx->start_download_time > 0){
					int start_time = d_ctx->start_download_time * AV_TIME_BASE;
					seek_time_int64t = av_rescale_q(start_time, AV_TIME_BASE_Q,
						in_stream->time_base);
				}

				int seek_res = -1;
	//			seek_res = avformat_seek_file(ifmt_ctx, i, INT64_MIN,
	//					seek_time_int64t, INT64_MAX, AVSEEK_FLAG_BACKWARD);
				if (seek_time_int64t > 0){
					seek_res = av_seek_frame(ifmt_ctx, i, seek_time_int64t, AVSEEK_FLAG_BACKWARD|AVSEEK_FLAG_FRAME);
//					LOG("download_thread_temp-->seek stream+++seek_time=%lld+++seek_res=%d", seek_time_int64t, seek_res);
				}
				else if(d_ctx->buffer_type_restart){
					seek_res = avformat_seek_file(ifmt_ctx, i, 0, seek_time_int64t, INT64_MAX, AVSEEK_FLAG_BACKWARD|AVSEEK_FLAG_FRAME);// AVSEEK_FLAG_BYTE|AVSEEK_FLAG_ANY
	//			    seek_res = av_seek_frame(ifmt_ctx, i, seek_time_int64t, AVSEEK_FLAG_BACKWARD|AVSEEK_FLAG_FRAME);
//					LOG("download_thread_temp-->restart seek stream+++seek_time=%lld+++seek_res=%d", seek_time_int64t, seek_res);
				}
			}

		}
    }
    
//    if(d_ctx->timeout > 3500)
//    	d_ctx->timeout = 3500;

    AVStream *in_stream;
    long lastTime;
    int has_checked_start_time = 0;
//    LOG("download_thread_temp 4");
    while (d_ctx) {
        if(!coredata_g || (coredata_g && coredata_g->exit))
        	break;

        pthread_mutex_lock(d_ctx->mutex);
        if (d_ctx->status == DOWNLOAD_STOP){
//        	LOG("download_thread_temp stopped break...");
        	pthread_mutex_unlock(d_ctx->mutex);
            break;
        }

        while (d_ctx->status == DOWNLOAD_PAUSE) {
//        	LOG("download_thread_temp is paused");
            pthread_cond_wait(d_ctx->cond, d_ctx->mutex);
//        	LOG("download_thread_temp end wait");

        }
        pthread_mutex_unlock(d_ctx->mutex);

        if (d_ctx->status == DOWNLOAD_STOP)
            break;

        d_ctx->timestamp = (long)(  av_gettime() / 1000);
//        	LOG("download_thread_temp before av_read_frame");
        ret = av_read_frame(ifmt_ctx, &pkt);
//        	LOG("download_thread_temp after av_read_frame");

        if (ret < 0){
//        	LOG("download_thread_temp read frame res = %d ",ret);
        	if(ret == -11)
        		goto release;
            break;
        }
//    	LOG("download_thread_temp read frame pkt.dts=%lld pkt.pts=%lld", pkt.dts, pkt.pts);

        if (d_ctx->status == DOWNLOAD_STOP){
    		goto release;
            break;
        }

        if(pkt.pts == INT64_MIN || pkt.dts == INT64_MIN){
        	goto release;
        }

        if (d_ctx->need_write_packet) {
            write_packet_to_file(&pkt, d_ctx); 
        }

        in_stream  = ifmt_ctx->streams[pkt.stream_index];

		int current = av_rescale_q(pkt.pts, in_stream->time_base,
				AV_TIME_BASE_Q) / AV_TIME_BASE;

		if (!has_checked_start_time){
			has_checked_start_time = 1;
//			LOG("download_thread_temp---set start pkt.pts=%lld,current is %d", pkt.pts,current);
			if(d_ctx->set_start_time)
				d_ctx->set_start_time(current);
		}
		long currentTime = (long)(  av_gettime() / 1000);
//        LOG("download_thread_temp while current_time=%d,pkt.pts=%lld,pkt.dts=%lld",current,pkt.pts,pkt.dts);
		if (d_ctx->status != DOWNLOAD_STOP && d_ctx->on_download_progress_changed && (current > current_downloaded_play_time || currentTime - lastTime >= 1000)) {
//			LOG("download_thread_temp---current is %d,current_downloaded_play_time is %d", current,current_downloaded_play_time);
			int64_t pos = 0;
			int64_t dts = 0;
			lastTime = currentTime;
			if (download_context_g){
				pos = download_context_g->current_bytes;
				dts = download_context_g->current_dts;
			}
			d_ctx->on_download_progress_changed(pos, dts, current, DOWNLOAD_PACKET_TEMP);
			current_downloaded_play_time = current;
			d_ctx->start_download_time = current;
		}
        if (ret < 0) {
            fprintf(stderr, "Error muxing packet\n");
            break;
        }
release:        
        pkt.pts = 0;
        av_free_packet(&pkt);

    }
//    LOG("download_thread_temp while stopped");
    if (ret == AVERROR_EOF){
//        av_write_trailer(ofmt_ctx);
        d_ctx->writed_to_eof = 1;
//	    if (d_ctx->on_download_finished)
//	        d_ctx->on_download_finished();
    } else {
    	char *toDelete = "a";
        fwrite(toDelete, sizeof("a"), 1, d_ctx->packet_file_write);
        int delete_result = remove(d_ctx->local_packet_file_path);
//        LOG("download_thread_temp delete_result=%d",delete_result);
        d_ctx->has_pkt_file_removed = 1;
    }
end:
    if (ret<0 && d_ctx->status != DOWNLOAD_STOP){//read_frame时退出，read返回值为负，此种情况不必让上层以为读取出错
    	if(ret != AVERROR_EOF){
			av_strerror(ret, error, 500);
			if (d_ctx->on_download_failed)
				d_ctx->on_download_failed(error);
    	}else{
    		if (d_ctx->on_download_finished)
    			d_ctx->on_download_finished();
    	}
//	    LOG("download_thread_temp error=%s",error);
    }
//    LOG("download_thread_temp avformat_close_input");
//    avformat_close_input(&ifmt_ctx);

    ifmt_ctx = NULL;

//    LOG("download_thread_temp download end");
    if (ret < 0 && ret != AVERROR_EOF) {
        //fprintf(stderr, "Error occurred: %s\n", av_err2str(ret));
        return 1;
    }
    return 0;
}
static void ff_log_callback(void*avcl, int level, const char*fmt, va_list vl)
{
//	LOG("ff_log is: %s",fmt);
}

static int download_thread(void *data)
{
//    pthread_t self_id;
//    self_id = pthread_self();
//    pthread_detach(self_id);

	int i, ret = -1;
    Download_context *d_ctx = (Download_context *)data;
    if (d_ctx == NULL)
        return ret;

    AVOutputFormat *ofmt = NULL;
    AVFormatContext *ifmt_ctx = NULL, *ofmt_ctx = NULL;
    AVPacket pkt;
    const char *in_filename, *out_filename;
	char error[500];

    in_filename  = d_ctx->network_url;
    out_filename = d_ctx->local_file_path;
    int current_downloaded_play_time;
//    LOG("out file is %s",out_filename);

    av_register_all();
    avformat_network_init();
    
    ifmt_ctx = avformat_alloc_context();
//    ifmt_ctx->interrupt_callback.callback = interrupt_cb;
//    ifmt_ctx->interrupt_callback.opaque = d_ctx;
    if(d_ctx->timeout > 1000){
    	d_ctx->timestamp = (long)(  av_gettime() / 1000);
//		LOG("download_thread_temp---current=%ld,timeout=%d",d_ctx->timestamp,d_ctx->timeout);
    }

//    LOG("download_thread 1");
    if ((ret = avformat_open_input(&ifmt_ctx, in_filename, 0, 0)) < 0) {//&options
		av_strerror(ret, error, 500);
//		LOG("download_streaming-->avformat_open_input res=%s...", error);
		goto end;
    }
//    d_ctx->is_input_opened = 1;

    d_ctx->timestamp = (long)(  av_gettime() / 1000);
//    LOG("download_thread 2");
    if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0) {
		av_strerror(ret, error, 500);
//		LOG("download_streaming-->avformat_find_stream_info res=%s...", error);
		goto end;
    }

//    LOG("download_thread 3");
    av_dump_format(ifmt_ctx, 0, in_filename, 0);

    d_ctx->streaming_duration = ifmt_ctx->duration / AV_TIME_BASE;

//    LOG("download_thread4 out_filename=%s", out_filename);
    ret = avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, out_filename);
    if (!ofmt_ctx) {
        av_strerror(ret, error, 500);
//        LOG("download_streaming-->avformat_alloc_output_context2 res=%s...", error);
        ret = AVERROR_UNKNOWN;
        goto end;
    }

    d_ctx->timestamp = (long)(  av_gettime() / 1000);
    ofmt = ofmt_ctx->oformat;
    int64_t seek_time_int64t;
    for (i = 0; i < ifmt_ctx->nb_streams; i++) {
        AVStream *in_stream = ifmt_ctx->streams[i];
        if (i == 0 && !d_ctx->is_live){
            if (d_ctx->start_download_time){
                int start_time = d_ctx->start_download_time * AV_TIME_BASE;
                seek_time_int64t = av_rescale_q(start_time, AV_TIME_BASE_Q,
                    in_stream->time_base);
            } else if (d_ctx->last_dts) {
                seek_time_int64t = d_ctx->last_dts;
            }

            int seek_res = -1;
            if (seek_time_int64t>0){
//                seek_res = avformat_seek_file(ifmt_ctx, i, INT64_MIN,
//                    seek_time_int64t, INT64_MAX, AVSEEK_FLAG_BACKWARD);
                seek_res = av_seek_frame(ifmt_ctx, i, seek_time_int64t, AVSEEK_FLAG_BACKWARD|AVSEEK_FLAG_FRAME);
//                LOG("download_streaming-->seek stream+++seek_time=%lld+++seek_res=%d", seek_time_int64t, seek_res);
            }
         }
        AVStream *out_stream = avformat_new_stream(ofmt_ctx, in_stream->codec->codec);
        if (!out_stream) {
//            LOG("Failed allocating output stream\n");
            ret = AVERROR_UNKNOWN;
            goto end;
        }

        ret = avcodec_copy_context(out_stream->codec, in_stream->codec);
        if (ret < 0) {
            fprintf(stderr, "Failed to copy context from input to output stream codec context\n");
            goto end;
        }
        out_stream->codec->codec_tag = 0;
        if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
            out_stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
        if(in_stream->codec->codec_type == AVMEDIA_TYPE_VIDEO){
//            LOG("input_stream-width=%d", in_stream->codec->width);
//            LOG("out_stream-width=%d", out_stream->codec->width);
        }
    }
    av_dump_format(ofmt_ctx, 0, out_filename, 1);

    d_ctx->timestamp = (long)(  av_gettime() / 1000);
//    LOG("download_thread 5");
    if (!(ofmt->flags & AVFMT_NOFILE)) {
        ret = avio_open(&ofmt_ctx->pb, out_filename, AVIO_FLAG_WRITE);
        if (ret < 0) {
            fprintf(stderr, "Could not open output file '%s'", out_filename);
            goto end;
        }
    }

//    LOG("download_thread 6");
    if (d_ctx->skip_bytes <= 0 || d_ctx->start_download_time > 0)
        ret = avformat_write_header(ofmt_ctx, NULL);
    if (ret < 0) {
        fprintf(stderr, "Error occurred when opening output file\n");
        goto end;
    }
    
    if (d_ctx->skip_bytes > 0 && d_ctx->start_download_time <= 0) {
//        LOG("download_thread write_existed...");
//    	write_existed(out_filename, ofmt_ctx ,d_ctx);
    	rewrite_exist_file(out_filename, ofmt_ctx ,d_ctx);
//    	fseek(d_ctx->packet_file_write, 0, SEEK_END);
//    	fseek(d_ctx->packet_file_read, 0, SEEK_SET);
    }

//    if(d_ctx->timeout > 3500)
//    	d_ctx->timeout = 3500;

    AVStream *in_stream, *out_stream;
//    LOG("download_thread 7");
	int flag_complete_second = 0;
    long lastTime;
//        av_log_set_level(48);
//        av_log_set_callback(ff_log_callback);
    while (d_ctx) {
        pthread_mutex_lock(d_ctx->mutex);
        if (d_ctx->status == DOWNLOAD_STOP && flag_complete_second)
            break;

        while (d_ctx->status == DOWNLOAD_PAUSE) {
            pthread_cond_wait(d_ctx->cond, d_ctx->mutex);
        }
        pthread_mutex_unlock(d_ctx->mutex);

        d_ctx->timestamp = (long)(  av_gettime() / 1000);
		flag_complete_second = 0;
        ret = av_read_frame(ifmt_ctx, &pkt);
        if (ret < 0){
//        	LOG("download_thread read frame error ret = %d", ret);
        	if(ret == -11)
        		goto release;
            break;
        }

        if(pkt.pts == INT64_MIN || pkt.dts == INT64_MIN){
        	goto release;
        }

        if(d_ctx->last_dts && d_ctx->last_dts >= pkt.dts)
        	continue;

        if (d_ctx->status == DOWNLOAD_STOP)
            break;

//        if (d_ctx->need_write_packet) {
//            write_packet_to_file(&pkt, d_ctx); 
//        }

        in_stream  = ifmt_ctx->streams[pkt.stream_index];
        out_stream = ofmt_ctx->streams[pkt.stream_index];

		d_ctx->current_dts = pkt.dts;//根据dts跳转的是input，必须在转换之前记录，否则如果input和output的timebase不一样的话会出问题
//        LOG("download_thread before change dts=%lld",pkt.dts);
		int current = av_rescale_q(pkt.pts, out_stream->time_base,
				AV_TIME_BASE_Q) / AV_TIME_BASE;

        /* copy packet */
        pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
        pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);

//        LOG("download_thread while dts=%lld",pkt.dts);
        pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);
        pkt.pos = -1;

        if (pkt.dts < 0 || pkt.pts < 0){
//        	LOG("download_thread---pts or dts < 0-----pts=%lld,dts=%lld",pkt.dts,pkt.dts);
//        	continue;
        }

        if(out_stream->cur_dts > pkt.dts){
//            LOG("download_thread while current_time=%d+++dts=%lld+++out_stream->cur_dts=%lld",current,pkt.dts,out_stream->cur_dts);
        	goto release;
        }
        else{
//			LOG("download_thread while current_time=%d+++dts=%lld+++pts=%lld",current,pkt.dts,pkt.pts);
			ret = av_interleaved_write_frame(ofmt_ctx, &pkt);
        }

		long currentTime = (long)(  av_gettime() / 1000);
//        LOG("download_thread while after current_time=%d",current);
		if (d_ctx->status != DOWNLOAD_STOP && download_context_temp == NULL && d_ctx->on_download_progress_changed && (current > current_downloaded_play_time || currentTime - lastTime >= 1000)) {
			lastTime = currentTime;
			flag_complete_second = 1;
			d_ctx->current_bytes = ofmt_ctx->pb->pos;
			d_ctx->on_download_progress_changed(ofmt_ctx->pb->pos,
					d_ctx->current_dts, current, DOWNLOAD_DEFAULT);
			current_downloaded_play_time = current;
		}
        if (ret < 0) {
//            LOG("download_thread while write packet error......ret=%d", ret);
            fprintf(stderr, "Error muxing packet\n");
            break;
        }
release:
        av_free_packet(&pkt);
        pkt.pts = 0;
    }
//    LOG("download_thread av_write_trailer");
    if (ret == AVERROR_EOF){
        av_write_trailer(ofmt_ctx);
        d_ctx->writed_to_eof = 1;
	    if (d_ctx->on_download_finished)
	        d_ctx->on_download_finished();
	    int delete_result = remove(d_ctx->local_packet_file_path);
//	    LOG("download_thread delete_result=%d",delete_result);
    }
end:
    if (ret<0){
	    av_strerror(ret, error, 500);
	    if (d_ctx->on_download_failed)
	    	d_ctx->on_download_failed(error);
//	    LOG("download_thread error=%s",error);
    }
//    LOG("download_thread avformat_close_input");
        avformat_close_input(&ifmt_ctx);

//    LOG("download_thread avio_closep");
    /* close output */
    if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE))
        avio_closep(&ofmt_ctx->pb);
//    LOG("download_thread avformat_free_context");
    avformat_free_context(ofmt_ctx);

//    LOG("download_thread download end");
    if (ret < 0 && ret != AVERROR_EOF) {
        //fprintf(stderr, "Error occurred: %s\n", av_err2str(ret));
        return 1;
    }
    return 0;
}

void set_download_status(Download_context *download_ctx, Download_status status)
{
//	LOG("set_download_status...status=%d",status);
    if (download_ctx) {
        pthread_mutex_lock(download_ctx->mutex);
        download_ctx->status = status;
//    	LOG("set_download_status...before signal");
        pthread_cond_signal(download_ctx->cond);
        pthread_mutex_unlock(download_ctx->mutex);
    }
//	LOG("set_download_status...end");
}

Download_status get_download_status(Download_context *download_ctx)
{
    Download_status status;
    pthread_mutex_lock(download_ctx->mutex);
    status = download_ctx->status;
    pthread_mutex_unlock(download_ctx->mutex);
    return status;
}

static FILE *open_file_write(const char *file)
{
    FILE *packet_file = NULL;

    packet_file = fopen(file, "ab+");

    return packet_file;
}

static FILE *open_file_read(const char *file)
{
    FILE *packet_file = NULL;

    packet_file = fopen(file, "rb+");

    return packet_file;
}

size_t read_packet_from_file(AVPacket *packet, Download_context *d_ctx)
{
    size_t ret = 0;
    Download_context *download_ctx = NULL;
    if (download_context_temp != NULL)
        download_ctx = download_context_temp; 
    else 
        download_ctx = d_ctx;

    if (download_ctx && download_ctx->packet_file_read) {
//    	LOG("writed = %d&&&readed = %d",download_ctx->writed_packet_nb,download_ctx->readed_packet_nb);

//    	if (download_ctx->writed_packet_nb - download_ctx->readed_packet_nb <= 1){
    	if (download_ctx->cur_file_pos <= ftell(download_ctx->packet_file_read) + 1000){
    		if(download_ctx->writed_to_eof)
    		    return AVERROR_EOF;
    		else
    		    return -2;
    	}

//    	LOG("read_packet_from_file packet = %p download_ctx->packet_file_read = %p", packet, download_ctx->packet_file_read);
//        LOG("read_packet_from_file packet->pts = %lld packet->dts = %lld packet->data = %p\n packet->stream_index = %d packet->size = %d packet->pos = %lld\n",
//            			packet->pts,packet->dts,packet->data,packet->stream_index,packet->size,packet->pos);
        ret = fread(packet, sizeof(AVPacket), 1, download_ctx->packet_file_read);
//        LOG("read_packet_from_file 1");
        packet->data = malloc(packet->size);
//        LOG("read_packet_from_file 2");
        ret = fread(packet->data, packet->size, 1, download_ctx->packet_file_read);
//        LOG("read_packet_from_file 3");
        if (packet->buf) {
            packet->buf = malloc(sizeof(AVBufferRef));
            ret = fread(packet->buf, sizeof(AVBufferRef), 1, download_ctx->packet_file_read);
//        	LOG("read_packet_from_file packet->buf->size = %d packet->buf = %p", packet->buf->size, packet->buf);
            packet->buf->data = packet->data;
//            LOG("read_packet_from_file 4");
//            packet->buf->buffer = malloc(sizeof(AVBuffer));
//            ret = fread(packet->buf->buffer, sizeof(AVBuffer), 1, download_ctx->packet_file_read);
//         //   packet->buf->
//            packet->buf->buffer->data = packet->data;
//            LOG("read_packet_from_file 5");
        }
        download_ctx->readed_packet_nb++;
//        LOG("read_packet_from_file packet->pts = %lld packet->dts = %lld read_numb=%d",packet->pts,packet->dts,download_ctx->readed_packet_nb);
    }

    return ret;
}

static size_t write_packet_to_file(AVPacket *packet, Download_context *download_ctx)
{
    size_t ret = 0;
    if (download_ctx && download_ctx->packet_file_write) {

        if (download_ctx->writed_packet_nb > download_ctx->packet_array_size) {
            download_ctx->packet_array_size *= 2;
            //TODO:fix realloc failed
            download_ctx->packet_array = (Packet_data *)realloc(download_ctx->packet_array, download_ctx->packet_array_size*sizeof(Packet_data));
        }

        if (download_ctx == download_context_g){
            download_ctx->packet_end_time = packet->pts;

            Packet_data *p_data = download_ctx->packet_array + download_ctx->writed_packet_nb;
            p_data->pts = packet->pts;
            p_data->dts = packet->dts;
            p_data->stream_index = packet->stream_index;
            p_data->pos = ftell(download_ctx->packet_file_write);

//            LOG("write_packet_to_file---p_data->pos=%lld", p_data->pos);
        }

        ret = fwrite(packet, sizeof(AVPacket), 1, download_ctx->packet_file_write);
        ret = fwrite(packet->data, packet->size, 1, download_ctx->packet_file_write);
        if (packet->buf) {
//        	LOG("write_packet_to_file packet->buf->size = %d packet->buf->data=%p packet->data=%p", packet->buf->size, packet->buf->data, packet->data);
            fwrite(packet->buf, sizeof(AVBufferRef), 1, download_ctx->packet_file_write);
        //    fwrite(packet->buf->buffer, sizeof(AVBuffer), 1, download_ctx->packet_file_write);
        }
        fflush(download_ctx->packet_file_write);
        download_ctx->cur_file_pos = ftell(download_ctx->packet_file_write);
        download_ctx->writed_packet_nb++;
        
//        LOG("write_packet_to_file packet->pts = %lld packet->dts = %lld packet->data = %p\n packet->stream_index = %d packet->size = %d packet->pos = %lld\n",
//                    			packet->pts,packet->dts,packet->data,packet->stream_index,packet->size,packet->pos);
    }
    return ret;
}

static void close_file(FILE *file)
{
    if (file)
        fclose(file);
}

int get_duration(Download_context *download_ctx) 
{
    if (download_ctx) {
        return download_ctx->streaming_duration;
    }
	return 0;
}

int get_start_downloaded_time()
{
    if (download_context_temp) {
        return download_context_temp->start_download_time;
    } else if(download_context_g){
    	return download_context_g->start_download_time;
    }
	return 0;
}

int seek_packet_file(Download_context *download_ctx_ori, int64_t seek_target, int stream_index)
{
//	LOG("seek_packet_file...");

//	LOG("seek_packet_file...seek_target =%lld",seek_target);
    if (download_ctx_ori != NULL && seek_target < download_ctx_ori->packet_end_time) {
//    	LOG("seek_packet_file...seek origin");
    	stop_temp_download_thread();
        //set download_ctx_ori file read pos to seek_target
        for (int i = 0; i < download_ctx_ori->writed_packet_nb - 1; ++i) {
            Packet_data p_data = download_ctx_ori->packet_array[i]; 
            Packet_data p_data1 = download_ctx_ori->packet_array[i+1]; 
            if (p_data.stream_index == stream_index && (seek_target >= p_data.pts || p_data1.pts <= seek_target)) {
                fseek(download_ctx_ori->packet_file_read, p_data.pos, SEEK_SET);
            }
        }

        //delete download_context_temp if not null

        return 1;
    } else {
//    	LOG("seek_packet_file...init new temp");
    	if (seek_target == 0)
    		seek_target = 100;//流媒体无法跳转至0，暂时跳转至100
    	if (download_ctx_ori)
    	    start_temp_download_thread(download_ctx_ori->network_url,str_join(download_ctx_ori->local_packet_file_path,".tmp"), seek_target, download_ctx_ori->timeout, 1);
    	else if (download_context_temp){
    		char *input_path , *pkt_path;
    		input_path = strdup(download_context_temp->network_url);
    		pkt_path = strdup(download_context_temp->local_packet_file_path);
    	    start_temp_download_thread(input_path,pkt_path, seek_target, download_context_temp->timeout, 1);
            MY_FREE(input_path);
            MY_FREE(pkt_path);
    	}
    }
    return 0;
}
#undef MY_FREE

void start_temp_download_thread(char *input_path, char *pkt_path, int64_t start_pos, int timeout, int is_restart){
//	stop_temp_download_thread();
	stop_temp_download();
//	LOG("start_temp_download_thread timeout=%d",timeout);
	download_context_temp = init_download(input_path, NULL, pkt_path);
	init_download_context_callback(download_context_temp);
	set_download_status(download_context_temp, DOWNLOAD_START);
	download_context_temp->type = DOWNLOAD_PACKET_TEMP;
	download_context_temp->last_dts = start_pos;//seek_array;
	download_context_temp->timeout = timeout;
	download_context_temp->buffer_type_restart = is_restart;
	start_download(download_context_temp);
}

void pause_temp_download_thread(){
//	LOG("pause_temp_download_thread...");
	if(download_context_temp){
		set_download_status(download_context_temp, DOWNLOAD_PAUSE);
	}
}

void stop_temp_download_thread(){
	if(download_context_temp){
	    deinit_download(download_context_temp);
	    download_context_temp = NULL;
	}
}

void stop_temp_download(){
//    if(coredata_g)
 //   	coredata_g->has_download_stopped = 1;
    stop_temp_download_thread();
}
